﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using ttbit.core;
using ttbit.math;

namespace ttbit.core
{
    public struct RenderInfo
    {
        public ttbit.core.RenderVertex[] buffer;
        public int seek;
    }
    //public interface IBatchRenderer
    //{
    //    //请求一个vbo空间，你把buf填进去就行
    //    RenderInfo ReqBuf(int rectcount, Material texture);
    //    void BatchDraw_End();
    //}
    public class QuadBatchRenderer
    {
        const int maxrectcount = 1000;
        public RenderPass pass = new RenderPass();
        RenderVertex[] vbo = new RenderVertex[maxrectcount * 4];
        int rectcount = 0;

        int[] ebo = new int[maxrectcount * 6];

        //public Vector2 panelScale = Vector2.one;
        //public Vector2 panelOffset = Vector2.zero;
        Stack<Rectangle> rectLimitStack = new Stack<Rectangle>();
        public QuadBatchRenderer()
        {
            for (var i = 0; i < ebo.Length / 6; i++)
            {
                ebo[i * 6 + 0] = i * 4 + 0;
                ebo[i * 6 + 1] = i * 4 + 1;
                ebo[i * 6 + 2] = i * 4 + 2;
                ebo[i * 6 + 3] = i * 4 + 2;
                ebo[i * 6 + 4] = i * 4 + 1;
                ebo[i * 6 + 5] = i * 4 + 3;
            }
            pass.UpdateEBO(ebo, maxrectcount * 6, true);
        }


        public RenderInfo ReqBuf(int needrectcount, Material material)
        {
            bool changepass = false;

            //检查
            if (rectcount + needrectcount > maxrectcount)
            {
                changepass = true;
            }

            if (pass.material != material)
            {
                changepass = true;
            }


            //绘制之前的
            if (changepass && rectcount > 0)
            {

                pass.UpdateVBO(vbo, rectcount * 4, true);

                pass.material.Apply();
                pass.Draw(rectcount * 6);
                rectcount = 0;
            }

            if (changepass)
            {
                pass.SetMaterial(material, true);
            }
            //if (pass.material.prog != shader)
            //{
            //    pass.SetProg(shader, true, true);
            //    UpdatePanelSize();

            //    var color = pass.material.GetUniform("color");
            //    color.value[0] = 1;
            //    color.value[1] = 1;
            //    color.value[2] = 1;
            //    color.value[3] = 1;
            //    var _texMain = pass.material.GetUniform("texmain");
            //    _texMain.texid = texture.TexID;
            //}
            //else if (texMain != null)
            //{
            //    texMain.texid = texture.TexID;
            //}
            int seek = rectcount;
            rectcount += needrectcount;
            return new RenderInfo() { buffer = vbo, seek = seek };

        }

        public void EndFrame()
        {
            if (rectcount > 0)
            {
                pass.UpdateVBO(vbo, rectcount * 4, true);
                //pass.material.Apply();
                pass.Draw(rectcount * 6);
            }
            rectcount = 0;
        }

        public void CheckLimit(int quadseek, int quadcount)
        {
            if (rectLimitStack.TryPeek(out var _limit))
            {
                for (var i = 0; i < quadcount; i++)
                {
                    LimitRect(vbo, (quadseek + i) * 4, _limit);
                }
            }

        }
        public void PushLimit(Rectangle _rect)
        {
            EndFrame();
            if (rectLimitStack.Count == 0)
            {
                rectLimitStack.Push(_rect);
                //    Env.GL.Enable(IGL.EnableCap.ScissorTest);
                //    Env.GL.Scissor(_rect.X, _rect.Y, _rect.Width, _rect.Height);
            }
            else
            {
                var last = rectLimitStack.Peek();
                var mixrect = Rectangle.Intersect(_rect, last);
                rectLimitStack.Push(mixrect);
                //    Env.GL.Scissor(mixrect.X, mixrect.Y, mixrect.Width, mixrect.Height);
            }

        }
        public void PopLimit()
        {
            EndFrame();
            rectLimitStack.Pop();
            //if(rectLimitStack.Count == 0)
            //{
            //    Env.GL.Disable(IGL.EnableCap.ScissorTest);
            //}
            //else
            //{
            //    var last = rectLimitStack.Peek();
            //    Env.GL.Scissor(last.X, last.Y, last.Width, last.Height);
            //}
        }
        //裁剪必须打断合批，所以硬裁剪和软裁剪分开用
        public static void LimitRect(RenderVertex[] vbo, int vertexseek, Rectangle limitRect)
        {
            float x1 = vbo[vertexseek].X;
            float x2 = vbo[vertexseek + 1].X;
            float y1 = vbo[vertexseek].Y;
            float y2 = vbo[vertexseek + 2].Y;
            if (x1 >= limitRect.X + limitRect.Width || x2 < limitRect.X
                ||
                y1 >= limitRect.Y + limitRect.Height || y2 < limitRect.Y)
            {
                vbo[vertexseek + 0].X = vbo[vertexseek + 2].X = 0;
                vbo[vertexseek + 1].X = vbo[vertexseek + 3].X = 0;
                vbo[vertexseek + 0].Y = vbo[vertexseek + 1].Y = 0;
                vbo[vertexseek + 2].Y = vbo[vertexseek + 3].Y = 0;
                return;
            }
            float wx = x2 - x1;
            float wy = y2 - y1;
            if (wx == 0 || wy == 0)
                return;
            float nx1 = x1;
            float nx2 = x2;
            float ny1 = y1;
            float ny2 = y2;
            if (x1 < limitRect.X)
            {
                nx1 = limitRect.X;
            }
            if (x2 > limitRect.X + limitRect.Width)
            {
                nx2 = limitRect.X + limitRect.Width;
            }
            if (y1 < limitRect.Y)
            {
                ny1 = limitRect.Y;
            }
            if (y2 > limitRect.Y + limitRect.Height)
            {
                ny2 = limitRect.Y + limitRect.Height;
            }


            vbo[vertexseek + 0].X = vbo[vertexseek + 2].X = nx1;
            vbo[vertexseek + 1].X = vbo[vertexseek + 3].X = nx2;
            vbo[vertexseek + 0].Y = vbo[vertexseek + 1].Y = ny1;
            vbo[vertexseek + 2].Y = vbo[vertexseek + 3].Y = ny2;

            float u1 = vbo[vertexseek].U;
            float u2 = vbo[vertexseek + 1].U;
            float v1 = vbo[vertexseek].V;
            float v2 = vbo[vertexseek + 2].V;

            float uw = u2 - u1;
            float vw = v2 - v1;
            float sx1 = (nx1 - x1) / wx;
            float sx2 = (nx2 - x1) / wx;
            float sy1 = (ny1 - y1) / wy;
            float sy2 = (ny2 - y1) / wy;
            vbo[vertexseek + 0].U = vbo[vertexseek + 2].U = u1 + uw * sx1;
            vbo[vertexseek + 1].U = vbo[vertexseek + 3].U = u1 + uw * sx2;
            vbo[vertexseek + 0].V = vbo[vertexseek + 1].V = v1 + vw * sy1;
            vbo[vertexseek + 2].V = vbo[vertexseek + 3].V = v1 + vw * sy2;
        }

    }
}
